#include "video_stream.h"
#include "demux.h"
#include "driver_audio.h"
#include "driver_sample.h"
#include "driver_hdec.h"

#ifndef _WIN32
#include "driver_lcd.h"
#include "driver_de.h"
#endif

#if 0
#define HDEC_LOCK	OAL_mutex_lock(&g_h264_context->mutex)
#define HDEC_UNLOCK	OAL_mutex_unlock(&g_h264_context->mutex)
#else
#define HDEC_LOCK
#define HDEC_UNLOCK
#endif

#define LCD_SIZE_X 360
#define LCD_SIZE_Y 360

typedef struct
{
	OAL_SEM mutex;
	void* h264;
	drv_cb_data_t cb_data;
	drv_hdec_buf_t hdec_buf;
}H264_CONTEXT;

H264_CONTEXT* g_h264_context = 0;

VIDEO_ERROR video_stream_init()
{
	if (g_h264_context)
	{
		return VIDEO_ERROR_01;
	}

	g_h264_context = (H264_CONTEXT*)MALLOC(sizeof(H264_CONTEXT));
	if (g_h264_context == 0)
	{
		return VIDEO_ERROR_02;
	}

	memset(g_h264_context, 0, sizeof(H264_CONTEXT));

	g_h264_context->mutex = OAL_mutex_create();
	if (g_h264_context->mutex == 0)
		return VIDEO_ERROR_12;

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR video_stream_free()
{
	if (g_h264_context)
	{
		if (g_h264_context->mutex)
		{
			OAL_mutex_destroy(&g_h264_context->mutex);
			g_h264_context->mutex = 0;
		}

		FREE(g_h264_context);
		g_h264_context = 0;
	}

	return VIDEO_ERROR_OK;
}

int H264_GetAnnexbNALU(unsigned char* inBuf, int bufLen)
{
	int pos = 0;
	unsigned char nal_unit_type = 0;
	int LeadingZero8BitsCount = 0;

	while ((pos < bufLen) && (inBuf[pos++] == 0));

	if ((pos + 3) >= bufLen) {
		return P_FRAME_BUF;
	}

	if (inBuf[pos - 1] != 1) {
		return P_FRAME_BUF;
	}

	if (pos < 3) {
		return P_FRAME_BUF;
	}
	else if (pos == 3) {
		LeadingZero8BitsCount = 0;
	}
	else {
		LeadingZero8BitsCount = pos - 4;
	}

	if (LeadingZero8BitsCount > 0) {
		return P_FRAME_BUF;
	}

	nal_unit_type = inBuf[pos] & 0x1f;
	if (nal_unit_type == 1) {
		return P_FRAME_BUF;
	}
	else {//check for I_SLICE
		return I_FRAME_BUF;
	}
}

int get_frame_type(FIL* favi, u32 frm_size)
{
	if (frm_size < 128)
	{
		return P_FRAME_BUF;
	}

	int type = P_FRAME_BUF;
	VIDEO_ERROR res = 0;
	u32 br = 0;
	FSIZE_t ofs = f_tell(favi);
	unsigned char inBuf[128];
	int bufLen = 128;
	res = f_read(favi, inBuf, 128, &br);
	if (res == 0 && br == 128)
	{
		type = H264_GetAnnexbNALU(inBuf, bufLen);
	}
	f_lseek(favi, ofs);
	return type;
}

VIDEO_ERROR on_h264_frame_out(FIL* favi, u32 frm_size)
{
	VIDEO_ERROR res = 0;
	u32 br = 0;

	handle_t handle = g_h264_context->h264;
	u32 res_size = frm_size;

	loff_t frame_type = get_frame_type(favi, frm_size);
	if (frame_type == I_FRAME_BUF)
	{
		if (dev_Ioctl(handle, HDEC_SET_IFRAME_SIZE, &frm_size))
			return VIDEO_ERROR_13;
	}
	else
	{
		frame_type = P_FRAME_BUF;
	}

	HDEC_LOCK;
	while (res_size)
	{
		dev_Ioctl(handle, HDEC_DQBUF, &g_h264_context->hdec_buf);
		u32 packet_bytes = g_h264_context->hdec_buf.len;
		u32 read_bytes = packet_bytes;
		if (read_bytes > res_size)
			read_bytes = res_size;

		res_size -= read_bytes;

		//if (res_size == 0)
		//	frame_type |= END_STREAM_BUF;

		res = f_read(favi, g_h264_context->hdec_buf.addr, read_bytes, &br);
		if (res || br != read_bytes)
		{
			HDEC_UNLOCK;
			return VIDEO_ERROR_99;
		}

		dev_Write(handle, (char*)(g_h264_context->hdec_buf.ram_id * packet_bytes), read_bytes, &frame_type);
		dev_Ioctl(handle, HDEC_QBUF, &g_h264_context->hdec_buf);
	}

	if (frm_size % 2)
	{
		char padding;
		f_read(favi, &padding, 1, &br);
	}

	return res;
}

VIDEO_ERROR video_stream_frame_out(FRAME* frame)
{
	VIDEO_ERROR res = 0;

	switch (g_avi_context->VideoType)
	{
	case AVI_FORMAT_MJPG:
		if (frame->h.size % 2)frame->h.size++;
		res = f_lseek(&g_video_context->favi, f_tell(&g_video_context->favi) + frame->h.size);
		if (res)return VIDEO_ERROR_08;
		break;

	case AVI_FORMAT_H264:
#if(0)
		res = f_lseek(&g_video_context->favi, f_tell(&g_video_context->favi) + frame->h.size);
		if (res)return VIDEO_ERROR_08;
#else
		res = on_h264_frame_out(&g_video_context->favi, frame->h.size);
#endif
		break;

	default:
		assert(0);
		break;
	}

	return VIDEO_ERROR_OK;
}

void h264_encoder_enable(bool flag)
{
	bool enable = flag;
	dev_Ioctl(g_h264_context->h264, SAMPLE_HENC_ENABLE, &enable);
}

void h264_encoder_sample_enable(bool flag)
{
	bool enable = flag;
	dev_Ioctl(g_h264_context->h264, SAMPLE_ENABLE, &enable);
}

void h264_encoder_lock_buffer(smaple_mm_t* mm)
{
	dev_Ioctl(g_h264_context->h264, SAMPLE_HENC_MALLOC, mm);

}

void h264_encoder_unlock_buffer(smaple_mm_t* mm)
{
	dev_Ioctl(g_h264_context->h264, SMAPLE_HENC_FREE, mm);
}

VIDEO_ERROR video_driver_pkg()
{
	VIDEO_ERROR res = 0;
	smaple_mm_t mm;
	h264_encoder_lock_buffer(&mm);
	res = mux_video_pkg(mm.addr, mm.len);
	h264_encoder_unlock_buffer(&mm);
	return res;
}

VIDEO_ERROR video_driver_frm()
{
	VIDEO_ERROR res = 0;
	res = video_driver_pkg();
	h264_encoder_enable(true);
	return res;
}

static void henc_pkt_irq_cb(void)
{
	VIDEO_MSG msg = { MSGID_H264_PKG, 0, 0 };
	if (g_video_context->queue1)OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
}

static void henc_frame_irq_cb(void)
{
	VIDEO_MSG msg = { MSGID_H264_FRM, 0, 0 };
	if (g_video_context->queue1)OAL_queue_send(&g_video_context->queue1, &msg, OAL_WAIT_FOREVER);
}

#ifndef _WIN32
static void _enable_de_output_path(handle_t handle, int outW, int outH, int frame_num, int crc, FIL* fp_out)
{
	de_config_t cfg;
	cfg.video_output_path = 0;
	cfg.video_source_x = outW;
	cfg.video_source_y = outH;
	cfg.video_resize_x = outW;
	cfg.video_resize_y = outH;
	//等比例缩放
	if(outW > outH) {
		if(outW > LCD_SIZE_X) {
			cfg.video_resize_x = LCD_SIZE_X;
			cfg.video_resize_y = (outH * LCD_SIZE_X)/outW;
		}
	} else {
		if(outH > LCD_SIZE_Y) {
			cfg.video_resize_y = LCD_SIZE_Y;
			cfg.video_resize_x = (outW * LCD_SIZE_Y)/outH;
		}
	}

	cfg.video_resize_x = cfg.video_resize_x & (~0x1);
	cfg.video_start_x = 0;
	cfg.video_start_y = 0;
	//居中显示
	if (cfg.video_resize_x < LCD_SIZE_X) {
		cfg.video_start_x = (LCD_SIZE_X - cfg.video_resize_x)/2;
	} 
	if (cfg.video_resize_y < LCD_SIZE_Y) {
		cfg.video_start_y = (LCD_SIZE_Y - cfg.video_resize_y)/2;
	}
	cfg.video_disp_x = LCD_SIZE_X;
	cfg.video_disp_y = LCD_SIZE_Y;
	//叠加UI显示
#ifdef VIDEO_OVERLAY_GUI
	cfg.gui_x_star = 80;
    cfg.gui_y_star = 200;
    cfg.gui_x = 200;
    cfg.gui_y = 60;
    cfg.gdma_len = 200 * 60 * 2;
    cfg.gdma_addr = (uint32_t)((unsigned int*)&logo[0]);
	cfg.alpha = 256;
	cfg.disp_mode = 2;
	#if ENABLE_MASK_MODE
	cfg.alpha = 256;
	cfg.mask_alpha = 0x0;
	cfg.mask_color = 0xf800;
	cfg.mask_enable = 1;
	#endif	
#else
	cfg.disp_mode = 1;
#endif
	dev_Ioctl(handle, DE_CONFIG, &cfg);
	OAL_sleep(500);
}
#endif

static void _hdec_irq_cb(int event, void* user_data)
{
	event = event;
	user_data = user_data;
	handle_t handle = g_h264_context->h264;
	dev_Ioctl(handle, HDEC_START, 0);
	HDEC_UNLOCK;
}

VIDEO_ERROR video_driver_open(int flag)
{
#ifdef VIDEO_720p
	driver_sample_henc_param para = {
		VIDEO_WIDTH,VIDEO_HEIGHT,1,2,33,
		768, 410, 20, 6, 6, 5, 2,
		0, 0, 0, 0, 
		30,4000,1,42,12,0
		// ,0xd090ea1a
	}; 
#elif defined VIDEO_VGA
	driver_sample_henc_param para = {
		VIDEO_WIDTH,VIDEO_HEIGHT,1,5,28,
		768, 410, 20, 6, 6, 5, 2,
		0, 0, 0, 0, 
		30,1000,1,42,12,0
		// ,0xd090ea1a
	};
#endif

	if (flag)
	{//decode
#ifndef _WIN32
		handle_t handle = dev_Open("/dev/lcd6806", 0);
		if(NULL == handle)
		{
			return VIDEO_ERROR_11;
			//logi("open /dev/tp failed except! ...\r\n");
		}
		_enable_de_output_path(handle, g_avi_context->Width, 
			g_avi_context->Height, 0, 0, 0);
#endif

		drv_hdec_cfg_t hdecCfg = { 0 };
		hdecCfg.param.h264_dec_w = g_avi_context->Width;
		hdecCfg.param.h264_dec_h = g_avi_context->Height;
		hdecCfg.param.num_p_of_gop = 4;
		hdecCfg.param.filter_enable = 1;
		hdecCfg.call_back = _hdec_irq_cb;
		hdecCfg.user_data = (void*)&g_h264_context->cb_data;
		g_h264_context->h264 = dev_Open("/dev/hdec", 0, &hdecCfg);
	}
	else
	{//encode
		driver_sample_open_opt_t opt = { 0 };
		opt.jenc_param = 0;
		opt.sample_henc_pkt_irq_cb = henc_pkt_irq_cb;
		opt.sample_henc_frame_irq_cb = henc_frame_irq_cb;
		opt.sample_jenc_frame_irq_cb = 0;
		opt.sample_jenc_pkt_irq_cb = 0;
		opt.dvp_param.width = VIDEO_WIDTH;
		opt.dvp_param.hight = VIDEO_HEIGHT;
		opt.dvp_param.yuv_mode = 1;
		opt.dvp_param.inputsrc = 0;

		OAL_memcpy(&opt.henc_param, &para, sizeof(driver_sample_henc_param));

		g_h264_context->h264 = dev_Open("/dev/sample", 0, &opt);

		h264_encoder_sample_enable(true);
	}

	if (g_h264_context->h264)
		return VIDEO_ERROR_OK;
	else
		return VIDEO_ERROR_11;
}

VIDEO_ERROR video_driver_start(int flag)
{
	handle_t handle = g_h264_context->h264;
	if (handle)
	{
		if (flag)
		{//decode
			dev_Ioctl(handle, HDEC_ENABLE, 0);
			dev_Ioctl(handle, HDEC_START, 0);
		}
		else
		{//encode
			h264_encoder_enable(true);
		}
	}

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR video_driver_close(int flag)
{
	handle_t handle = g_h264_context->h264;
	if (handle)
	{
		if (flag)
		{//decode
			HDEC_LOCK;
			dev_Ioctl(handle, HDEC_STOP, 0);
			dev_Close(&handle);
			HDEC_UNLOCK;
		}
		else
		{//encode
			h264_encoder_sample_enable(false);
			h264_encoder_enable(false);
			dev_Close(&handle);
		}
	}

	return VIDEO_ERROR_OK;
}
